home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Languguage OS 2
/
Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO
/
language
/
ici
/
ici.cpi
/
object.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-10-27
|
12KB
|
572 lines
#include "exec.h"
#include "buf.h"
#include "int.h"
#include "str.h"
#include "float.h"
#include "func.h"
#ifdef MULTI
#include "multi.h"
#endif
#define VERBOSE 0
/*
* The global error message pointer.
*/
char *error;
/*
* All objects are either on the objects list on in the atom pool.
*/
object_t **objs; /* List of all objects. */
object_t **objs_limit; /* First element we can't use in list. */
object_t **objs_top; /* Next unused element in list. */
object_t **atoms; /* Hash table of lists of atomic objects. */
int atomsz; /* Number of slots in hash table. */
STATIC int natoms; /* Number of atomic objects. */
/*
* Some primes just less than powers of two. We use these for
* successive sizes of the atom pool (a hash table).
*/
static int atomzi; /* Current index into... */
static unsigned long atomzs[] =
{
61, 127, 251, 509, 1021,
2039, 4093, 8191, 16381, 32749,
65521, 131071, 262139, 524287, 1048573,
(2<<21)-1,(2<<22)-1,(2<<23)-1,(2<<24)-1,(2<<25)-1,
(2<<26)-1,(2<<27)-1,(2<<28)-1,(2<<29)-1,(2<<30)-1,
(2<<31)-1
};
/*
* Format a human readable version of the object in 30 chars or less.
*/
char *
objname(p, o)
register char *p;
register object_t *o;
{
extern char *strchr();
if (isstring(o))
{
if (stringof(o)->s_nchars > 10)
sprintf(p, "\"%.10s...\"", stringof(o)->s_chars);
else
sprintf(p, "\"%s\"", stringof(o)->s_chars);
}
else if (isint(o))
sprintf(p, "%ld", intof(o)->i_value);
else if (isfloat(o))
sprintf(p, "%g", floatof(o)->f_value);
else if (isfunc(o) && (o->o_flags & O_CFUNC) && funcof(o)->f_name != NULL)
sprintf(p, "%s()", funcof(o)->f_name->s_chars);
else if (isfunc(o) && !(o->o_flags & O_CFUNC) && cfuncof(o)->cf_name !=NULL)
sprintf(p, "%s()", cfuncof(o)->cf_name);
else if (strchr("aeiou", o->o_type->t_name[0]) != NULL)
sprintf(p, "an %s", o->o_type->t_name);
else
sprintf(p, "a %s", o->o_type->t_name);
return p;
}
void
free_simple(o)
register object_t *o;
{
zfree((char *)o);
}
object_t *
copy_simple(o)
object_t *o;
{
return o;
}
int
assign_simple(o, k, v)
object_t *o;
object_t *k;
object_t *v;
{
char n1[30];
char n2[30];
char n3[30];
sprintf(buf, "attempt to set %s keyed by %s to %s",
objname(n1, o),
objname(n2, k),
objname(n3, v));
error = buf;
return 1;
}
object_t *
fetch_simple(o, k)
object_t *o;
object_t *k;
{
char n1[30];
char n2[30];
sprintf(buf, "attempt to read %s keyed by %s",
objname(n1, o),
objname(n2, k));
error = buf;
return NULL;
}
/*
* For objects which can't be copied and are intrinsically unique.
*/
int
cmp_unique(o1, o2)
object_t *o1;
object_t *o2;
{
return o1 != o2;
}
long
hash_unique(o)
object_t *o;
{
return (long)o;
}
/*
* Grow the hash table of atoms to be four times as big, plus three.
*/
STATIC void
grow_atoms()
{
register object_t **o;
register int i;
object_t **olda;
int newz;
newz = atomzs[atomzi + 1];
if ((o = (object_t **)zalloc(newz * sizeof(object_t *))) == NULL)
return;
++atomzi;
i = atomsz;
atomsz = newz;
memset((char *)o, 0, newz * sizeof(object_t *));
natoms = 0;
olda = atoms;
atoms = o;
while (--i >= 0)
{
if (olda[i] != NULL)
{
olda[i]->o_flags &= ~O_ATOM;
atom(olda[i], 1);
}
}
zfree((char *)olda);
}
/*
* Return an object equal to the one given, but possibly shared by others.
* Never fails, at worst it just returns its argument. If the lone flag
* is given, the object is free'd if it isn't used. ("lone" because the
* caller has the lone reference to it and will replace that with what
* atom returns anyway.) If the lone flag is not given, and the object
* would be used, a copy will be used. Also note that if the given and
* the object is not used, the refs of the passed object will be transfered
* to the object being returned.
*/
object_t *
atom(o, lone)
register object_t *o;
int lone;
{
register object_t **po;
if (o->o_flags & O_ATOM)
return o;
for
(
po = &atoms[((unsigned long)o->o_type + hash(o)) % atomsz];
*po != NULL;
--po < atoms ? po = atoms + atomsz - 1 : NULL
)
{
if (o->o_type == (*po)->o_type && cmp(o, *po) == 0)
{
if (lone)
{
(*po)->o_nrefs += o->o_nrefs;
o->o_nrefs = 0;
}
return *po;
}
}
/*
* Not found. Add this object (or a copy of it) to the atom pool.
*/
if (!lone)
{
if ((*po = copy(o)) == NULL)
return o;
o = *po;
}
*po = o;
o->o_flags |= O_ATOM;
if (++natoms > atomsz / 2)
grow_atoms();
if (!lone)
loose(o);
return o;
}
/*
* Probe the atom pool for an atomic form of o. If found, return that
* atomic form, else NULL. Used by various new_*() routines. These
* routines generally set up a dummy version of the object being made
* which is passed to this probe. If it finds a match, that is returned,
* thus avoiding the allocation of an object will may be thrown away anyway.
*/
object_t *
atom_probe(o)
register object_t *o;
{
register object_t **po;
for
(
po = &atoms[((unsigned long)o->o_type + hash(o)) % atomsz];
*po != NULL;
--po < atoms ? po = atoms + atomsz - 1 : NULL
)
{
if (o->o_type == (*po)->o_type && cmp(o, *po) == 0)
return *po;
}
return NULL;
}
/*
* Quick search for an int to save allocation/deallocation if it already
* exists.
*/
int_t *
atom_int(i)
register long i;
{
register object_t *o;
register object_t **po;
/*
* NB: There is an in-line version of this code in binop.h
*/
for
(
po = &atoms[((unsigned long)&int_type + i * 7) % atomsz];
(o = *po) != NULL;
--po < atoms ? po = atoms + atomsz - 1 : NULL
)
{
if (isint(o) && intof(o)->i_value == i)
return intof(o);
}
return NULL;
}
STATIC void
unatom(o)
object_t *o;
{
register object_t **sl;
register object_t **ss;
register object_t **ws; /* Wanted position. */
for
(
ss = &atoms[((unsigned long)o->o_type + hash(o)) % atomsz];
*ss != NULL;
--ss < atoms ? ss = atoms + atomsz - 1 : NULL
)
{
if (o == *ss)
goto delete;
}
/*printf("Warning: could not find atom being deleted\n");*/
return;
delete:
o->o_flags &= ~O_ATOM;
--natoms;
sl = ss;
/*
* Scan "forward" bubbling up entries which would rather be at our
* current empty slot.
*/
for (;;)
{
if (--sl < atoms)
sl = atoms + atomsz - 1;
if (*sl == NULL)
break;
ws = &atoms[((unsigned long)(*sl)->o_type + hash(*sl)) % atomsz];
if
(
(sl < ss && (ws >= ss || ws < sl))
||
(sl > ss && (ws >= ss && ws < sl))
)
{
/*
* The value at sl, which really wants to be at ws, should go
* into the current empty slot (ss). Copy it to there and update
* ss to be here (which now becomes empty).
*/
*ss = *sl;
ss = sl;
}
}
*ss = NULL;
}
object_t *
grow_objs(o)
object_t *o;
{
register object_t **newobjs;
register int newz;
newz = 2 * (objs_limit - objs) + 1;
if ((newobjs = (object_t **)zalloc(newz * sizeof(object_t *))) == NULL)
return o;
memcpy((char *)newobjs, (char *)objs, (char *)objs_limit - (char *)objs);
objs_limit = newobjs + newz;
objs_top = newobjs + (objs_top - objs);
memset((char *)objs_top, 0, (char *)objs_limit - (char *)objs_top);
zfree((char *)objs);
objs = newobjs;
*objs_top++ = o;
return o;
}
/*
* Mark sweep garbage collection. Should be safe to do any time, as new
* objects are created without the nrefs == 0 which allows them to be
* collected. They must be explicitly lost before they are subject
* to garbage collection. But of course all code must be careful not
* to hang on to "found" objects where they are not accessible, or they
* will be collected. You can got() them if you want. All "held" objects
* will cause all objects referenced from them to be marked (ie, not
* collected), as long as they are registered on either the global object
* list or in the atom pool. Thus statically declared objects which
* reference other objects (very rare) must be appropriately registered.
*/
void
collect()
{
register object_t **a;
register object_t *o;
register object_t **b;
register int ndead_atoms;
register long mem; /* Total mem tied up in refed objects. */
/*
* Mark all objects which are referenced (and thus what they ref).
*/
mem = 0;
#ifdef MULTI
{
register proc_vars_t *pv;
for (pv = main_vars; pv; pv = pv->v_next)
{
objof(&pv->pv_ex.x_xs)->o_flags &= ~O_MARK;
objof(&pv->pv_ex.x_os)->o_flags &= ~O_MARK;
objof(&pv->pv_ex.x_vs)->o_flags &= ~O_MARK;
mem += mark(&pv->pv_ex.x_xs);
mem += mark(&pv->pv_ex.x_os);
mem += mark(&pv->pv_ex.x_vs);
}
}
#endif
for (a = objs; a < objs_top; ++a)
{
if ((*a)->o_nrefs != 0)
mem += mark((*a));
}
/*
* Count how many atoms are going to be retained and how many are
* going to be lost so we can decide on the fastest method.
*/
ndead_atoms = 0;
for (a = objs; a < objs_top; ++a)
{
if (((*a)->o_flags & (O_ATOM|O_MARK)) == O_ATOM)
++ndead_atoms;
}
/*
* Collection phase. Discard unmarked objects, compact down marked
* objects and fix up the atom pool.
*
* Deleteing an atom from the atom pool is (say) once as expensive
* as adding one. Use this to determine which is quicker; rebuilding
* the atom pool or deleting dead ones.
*/
if (1 * ndead_atoms > (natoms - ndead_atoms))
{
/*
* Rebuilding the atom pool is a better idea.
*/
memset((char *)atoms, 0, atomsz * sizeof(object_t *));
natoms = 0;
b = objs;
for (a = objs; a < objs_top; ++a)
{
if (((o = *a)->o_flags & O_MARK) == 0)
freeo(o);
else
{
o->o_flags &= ~O_MARK;
*b++ = o;
if (o->o_flags & O_ATOM)
{
o->o_flags &= ~O_ATOM;
atom(o, 1);
}
}
}
objs_top = b;
}
else
{
/*
* Faster to delete dead atoms as we go.
*/
b = objs;
for (a = objs; a < objs_top; ++a)
{
if (((o = *a)->o_flags & O_MARK) == 0)
{
if (o->o_flags & O_ATOM)
unatom(o);
freeo(o);
}
else
{
o->o_flags &= ~O_MARK;
*b++ = o;
}
}
objs_top = b;
}
/*printf("mem %ld objects %d got %d atoms %d\n", mem, objs_top - objs, ngot, natoms);*/
/*
* The amount of memory tied up in referenced objects after a collect
* helps control the pace of garbage collection. We keep a nominal
* minimum value to stop collection when we aren't using much memory.
*/
if (mem < 2048)
ici_old_mem = 2048;
else
ici_old_mem = mem;
ici_new_mem = 0;
}
#ifdef BUGHUNT
got(o)
{
if (++objof(o)->o_nrefs > 10)
{
printf("Warning: nrefs > 10\n");
fflush(stdout);
}
}
loose(o)
{
if (--objof(o)->o_nrefs < 0)
{
printf("Warning: nrefs < 0\n");
fflush(stdout);
}
}
#endif
#ifdef SMALL
long
mark(o)
object_t *o;
{
if (o->o_flags & O_MARK)
return 0L;
return (*o->o_type->t_mark)(o);
}
void
freeo(o)
object_t *o;
{
(*o->o_type->t_free)(o);
}
long
hash(o)
object_t *o;
{
return (*o->o_type->t_hash)(o);
}
int
cmp(o1, o2)
object_t *o1;
object_t *o2;
{
return (*o1->o_type->t_cmp)(o1, o2);
}
object_t *
copy(o)
object_t *o;
{
return (*o->o_type->t_copy)(o);
}
object_t *
fetch(o, k)
object_t *o;
object_t *k;
{
return (*o->o_type->t_fetch)(o, k);
}
int
assign(o, k, v)
object_t *o;
object_t *k;
object_t *v;
{
return (*o->o_type->t_assign)(o, k, v);
}
void
rego(o)
object_t *o;
{
if (objs_top < objs_limit)
*objs_top++ = o;
else
grow_objs(o);
}
#endif